import threading
import requests
from PIL import Image, ImageTk
from io import BytesIO
import tkinter as tk
import time
import tkinter.messagebox
import tkinter.filedialog
import os
import logging
import json


class AddonsLibrary:
    def __init__(self,namespace : str):
        self.namespace = namespace
        logger.debug(f"初始化插件库: {self.namespace}")
    def GetResourceDirPath(self,res):
        return f"{os.getcwd()}/addons/{self.namespace}/{self.namespace}/{res}"



logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 配置日志记录，帮助调试
file_handler = logging.FileHandler(
    'logs/app_debug.log',
      mode='w', 
      encoding='utf-8'
)

formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
# 将文件处理器添加到logger中
logger.addHandler(file_handler)

logger.debug(f"globals: {globals()}")

# 全局URL变量
global URL
URL = ""

def login(root, count):
    global URL
    try:
        URL = count.strip()  # 去除可能的空格
        logging.debug(f"尝试连接服务器: {URL}")
        
        if not URL:
            tkinter.messagebox.showerror("错误", "请输入服务器地址")
            return
        
        # 尝试简单的连接测试
        test_url = f"http://{URL}"
        response = requests.get(test_url, timeout=5)
        if response.status_code == 200:
            logging.debug("服务器连接测试成功")
            root.destroy()
        else:
            tkinter.messagebox.showerror("连接失败", f"服务器无响应，状态码: {response.status_code}")
            logging.error(f"服务器无响应，状态码: {response.status_code}")
            
    except Exception as e:
        tkinter.messagebox.showerror("连接错误", f"无法连接到服务器: {str(e)}")
        logging.error(f"登录过程出错: {str(e)}")

def main():
    try:
        logging.debug("程序开始运行")
        logging.debug("创建登录窗口")
        # 创建登录窗口
        root4 = tk.Tk()
        root4.title("连接服务器")
        root4.geometry("400x150")
        # 确保窗口显示在屏幕中央
        root4.update_idletasks()
        width = root4.winfo_width()
        height = root4.winfo_height()
        x = (root4.winfo_screenwidth() // 2) - (width // 2)
        y = (root4.winfo_screenheight() // 2) - (height // 2)
        root4.geometry('{}x{}+{}+{}'.format(width, height, x, y))
        
        tk.Label(root4, text="请输入服务器地址").pack(pady=5)
        entry1 = tk.Entry(root4, width=40)
        entry1.pack(pady=5)
        login_button = tk.Button(root4, text="连接", command=lambda: login(root4, entry1.get()))
        login_button.pack(pady=5)
        
        logging.debug("登录窗口创建完成，进入主循环")
        root4.mainloop()
        
        # 确保URL已设置
        global URL
        if not URL:
            logging.error("未设置服务器地址，程序将退出")
            tkinter.messagebox.showerror("错误", "未设置服务器地址，程序将退出")
            return
        
        IMAGE_URL = f"http://{URL}/Desktop"
        logging.debug(f"图像URL: {IMAGE_URL}")

        global _user_agent_cache
        _user_agent_cache = None

        def get_user_agent() -> str:
            """返回用户代理字符串"""
            global _user_agent_cache
            if _user_agent_cache is not None:
                return _user_agent_cache
            else:
                _user_agent_cache = "lc/" + "2.0".strip()
                return _user_agent_cache

        def check_path_type(path):
            """检查远程路径的类型（文件或文件夹）"""
            check_URL = f"http://{URL}/CheckPathType/{path}"
            try:
                response = requests.get(check_URL, headers={"user-agent": get_user_agent()})
                response.raise_for_status()
                data = response.json()
                return data.get("type")
            except Exception as e:
                logging.error(f"检查路径类型失败: {e}")
                return None
        global ImageApp
        class ImageApp:
            def __init__(self, root):
                self.root = root
                self.root.title("远程桌面 - 连接至: " + URL)
                self.root.geometry("800x600")  # 设置窗口大小为800x600
                logging.info("加载插件")
                addons = []
                addons = os.listdir("addons")
                logging.debug(f"发现{len(addons)}个插件，{addons}")
                logging.info("开始解析插件")
                menubar = tk.Menu(self.root)
                addons_menu = tk.Menu(self.root)
                menubar.add_cascade(label="插件", menu=addons_menu)
                for addon in addons:
                    logging.debug(f"解析插件: {addon}")
                    path = os.path.join("addons", addon)
                    with open(path+"/addons.json","r", encoding="utf-8") as mef:
                        mef_content = mef.read()
                    mef_content = json.loads(mef_content)
                    logging.debug(f"插件信息(json): \n{mef_content}")
                    namespace = mef_content["namespace"]
                    orgin_main = mef_content["main"]
                    version = mef_content["version"]
                    name = mef_content["name"]
                    info_author = mef_content["info"]["author"]
                    info_email = mef_content["info"]["email"]
                    info_description = mef_content["info"]["description"]
                    addons_type = mef_content["type"]
                    logging.debug(f"插件信息: \nnamespace: {namespace},\nmain: {orgin_main}, \nversion: {version},\nname: {name},\nanthor: {info_author},\nemail: {info_email},\ndescription: {info_description},\ntype: {addons_type}")

                    # 加载插件主程序
                    logging.debug(f"Path: {path}")
                    with open(path+"/"+namespace+"/"+orgin_main, "r", encoding="utf-8") as f:
                        main_content = f.read()
                    exec(main_content, globals())
                    logging.debug(f"插件主程序加载成功: {orgin_main}")
                    addons_menu.add_command(label=name, command=lambda: exec(f"{namespace}Main()"))


                
                    
                
                filemenu = tk.Menu(menubar, tearoff=0)
                filemenu.add_command(label="查看远程机文件", command=self.check_file)
                filemenu.add_command(label="在远程机上运行命令", command=self.runcmd)
                filemenu.add_separator()
                filemenu.add_command(label="退出", command=self.on_close)
                menubar.add_cascade(label="远程操作", menu=filemenu)
                menubar.add_command(label="插件管理", command=self.install_addon)
                menubar.add_command(label="关于", command=self.show_about)
                self.root.config(menu=menubar)

                self.label = tk.Label(root)
                self.label.pack(expand=True, fill=tk.BOTH)
                self.imgtk = None
                self.running = True
                
                # 启动图像更新线程
                self.update_thread = threading.Thread(target=self.update_image, daemon=True)
                self.update_thread.start()
                logging.debug("主窗口初始化完成")
            def install_addon(self):
                ruitls = requests.get("https://gitee.com/ocsoft/LC-addons-Database/raw/master/index.json",headers={"user-agent": get_user_agent()})
                ruitls.raise_for_status()
                data = ruitls.json()
                logging.debug(f"插件库数据: {data}")
                root5 = tk.Toplevel(self.root)
                root5.title("插件管理")
                installLocalButton = tk.Button(root5, text="本地安装插件", command=lambda : self.install_addon_local())
                installOnlineButton = tk.Button(root5, text="在线安装插件", command=lambda : self.install_addon_online(data))
                installLocalButton.pack(pady=5)
                installOnlineButton.pack(pady=5)
            
            def install_addon_local(self):
                filepath = tkinter.filedialog.askopenfilename(title="选择要安装的插件")
                if not filepath:
                    tkinter.messagebox.showwarning("警告", "未选择插件文件")
                    return
                try:
                    # 扩展名为zca,ZIP压缩包,解压到addons目录下
                    if not filepath.endswith(".zca"):
                        tkinter.messagebox.showerror("错误", "请选择正确的插件文件(.zca)")
                        return
                    import zipfile
                    with zipfile.ZipFile(filepath, 'r') as zip_ref:
                        zip_ref.extractall("addons")
                    tkinter.messagebox.showinfo("成功", "插件安装成功！")
                except Exception as e:
                    tkinter.messagebox.showerror("错误", f"插件安装失败: {str(e)}")
                    logging.error(f"插件安装失败: {str(e)},PATH: {filepath}")
            def show_about(self):
                tkinter.messagebox.showinfo("关于", "远程桌面客户端\n版本: 2.0\n作者: fhx\n邮箱: aa\n描述: ads")
            def runcmd(self):
                """在远程机器上运行命令"""
                root3 = tk.Toplevel(self.root)
                root3.title("远程命令执行")
                root3.geometry("500x150")
                root3.transient(self.root)  # 设置为主窗口的子窗口
                root3.grab_set()  # 模态窗口

                tk.Label(root3, text="请输入要在远程机执行的命令:").pack(pady=5)
                cmd_entry = tk.Entry(root3, width=60)
                cmd_entry.pack(pady=5)
                cmd_entry.focus_set()

                def run():
                    cmd = cmd_entry.get().strip()
                    if not cmd:
                        tkinter.messagebox.showwarning("警告", "请输入命令")
                        return

                    CMD_URL = f"http://{URL}/RunCmd?cmd={cmd}"
                    try:
                        response = requests.get(CMD_URL, headers={"user-agent": get_user_agent()})
                        response.raise_for_status()
                        data = response.json()
                        cmd_output = data.get("cmd", "命令执行成功，但未返回结果")
                        tkinter.messagebox.showinfo("命令执行完成", cmd_output)
                        root3.destroy()
                    except Exception as e:
                        tkinter.messagebox.showerror("错误", f"在远程机运行命令时发生错误: {str(e)}")
                        logging.error(f"命令执行错误: {str(e)}")

                run_button = tk.Button(root3, text="运行", command=run)
                run_button.pack(pady=5)
                
                # 按Enter键执行命令
                cmd_entry.bind("<Return>", lambda event: run())

            def check_file(self):
                root2 = tk.Toplevel(self.root)
                root2.title("查看远程机文件")
                root2.geometry("400x300")
                PATH = "D:\\"
                LISTDIR = "http://{URL}//ListDir/{PATH}".format(URL=URL, PATH=PATH)

                def list_dir(path):
                    item = path
                    full_path = (item).replace("/", "\\")
                    if check_path_type(full_path) == "folder" or True:
                        LISTDIR = "http://{URL}//ListDir/{PATH}".format(URL=URL, PATH=full_path)
                        try:
                            response = requests.get(LISTDIR, headers={"user-agent": get_user_agent()})
                            response.raise_for_status()
                            data = response.json()
                            if "error" not in dict(data).keys():
                                root2.title(f"查看远程机文件: {PATH}")
                                print(f"URL: {LISTDIR}", data)
                                listbox.delete(0, tk.END)  # 清空当前列表
                                text.delete(0, tk.END)  # 清空路径输入框
                                text.insert(0, full_path)  # 更新路径输入框
                                for item in data["dir"]:
                                    listbox.insert(tk.END, item)
                                listbox.pack(expand=True)
                                global updata_file_button
                                updata_file_button["text"] = f"上传文件至 {PATH}"
                            else:
                                print(f"下载文件: {full_path}")
                                DOWNLOAD_URL = "http://{URL}/RawFile/{PATH}".format(URL=URL, PATH=full_path)
                                try:
                                    response = requests.get(DOWNLOAD_URL, headers={"user-agent": get_user_agent()})
                                    response.raise_for_status()
                                    item = full_path.split("\\")[-1]
                                    path = tkinter.filedialog.askdirectory()
                                    if not path:
                                        return
                                    item = path + "\\" + item
                                    with open(item, "wb") as f:
                                        f.write(response.content)
                                    tkinter.messagebox.showinfo("下载完成", f"文件已下载到当前目录: {path+item}")
                                except Exception as e:
                                    tkinter.messagebox.showinfo("错误", f"无法下载文件: {e}")
                        except Exception as e:
                            tkinter.messagebox.showinfo("错误", f"无法打开文件夹: {e}")

                try:
                    def updata_file_fun():
                        filepath = tkinter.filedialog.askopenfilename(title="选择要上传的文件")
                        if not filepath:
                            return

                        try:
                            # 获取文件名
                            filename = os.path.basename(filepath)
                            
                            # 读取文件内容
                            with open(filepath, 'rb') as f:
                                file_content = f.read()

                            # 构造请求
                            UPDATA_URL = f"http://{URL}/UpdataFile"
                            files = {
                                'file': (filename, file_content, 'application/octet-stream')
                            }
                            data = {
                                'filename': filename  # 只传文件名，路径由服务端决定
                            }

                            # 发送请求
                            response = requests.post(
                                UPDATA_URL,
                                headers={
                                    "user-agent": get_user_agent(),
                                    "Content-Type": "multipart/form-data"
                                },
                                files=files,
                                data=data,
                                timeout=30
                            )
                            
                            # 处理响应
                            response.raise_for_status()
                            result = response.json()
                            
                            if result.get('error'):
                                raise Exception(result['error'])
                                
                            tkinter.messagebox.showinfo("成功", f"文件 {filename} 上传成功！")
                            
                        except requests.exceptions.RequestException as e:
                            tkinter.messagebox.showerror("网络错误", f"无法连接服务器: {str(e)}")
                        except Exception as e:
                            tkinter.messagebox.showerror("上传错误", f"上传失败: {str(e)}")



                    PATH = PATH.replace("/", "\\")
                    response = requests.get(LISTDIR, headers={"user-agent": get_user_agent()})
                    response.raise_for_status()
                    data = response.json()
                    global updata_file_button
                    updata_file_button = tk.Button(root2, text=f"上传文件至 {PATH}", command=updata_file_fun)
                    # updata_file_button.pack(pady=5)  # 上传文件先弃更，先写别的。
                    text = tk.Entry(root2, width=40)
                    text.insert(0, PATH)
                    text.pack()
                    text.bind("<Return>", lambda event: list_dir(text.get()))
                    listbox = tk.Listbox(root2, width=40, height=20)
                    root2.title(f"查看远程机文件: {PATH}")
                    for item in data["dir"]:
                        listbox.insert(tk.END, item)
                    listbox.pack(expand=True)

                    # 绑定双击事件
                    def on_double_click(event):
                        selection = listbox.curselection()
                        if selection:
                            item = listbox.get(selection[0])
                            PATH = text.get()
                            full_path = PATH + "/" + item
                            full_path = full_path.replace("/", "\\")
                            if True:
                                LISTDIR = "http://{URL}//ListDir/{PATH}".format(URL=URL, PATH=full_path)
                                try:
                                    response = requests.get(LISTDIR, headers={"user-agent": get_user_agent()})
                                    response.raise_for_status()
                                    data = response.json()
                                    if "error" not in dict(data).keys():
                                        root2.title(f"查看远程机文件: {PATH}")
                                        print(f"URL: {LISTDIR}", data)
                                        listbox.delete(0, tk.END)  # 清空当前列表
                                        text.delete(0, tk.END)  # 清空路径输入框
                                        text.insert(0, full_path)  # 更新路径输入框
                                        for item in data["dir"]:
                                            listbox.insert(tk.END, item)
                                        listbox.pack(expand=True)
                                        global updata_file_button
                                        updata_file_button["text"] = f"上传文件至 {PATH}"
                                    else:
                                        print(f"下载文件: {full_path}")
                                        DOWNLOAD_URL = "http://{URL}/RawFile/{PATH}".format(URL=URL, PATH=full_path)
                                        try:
                                            response = requests.get(DOWNLOAD_URL, headers={"user-agent": get_user_agent()})
                                            response.raise_for_status()
                                            item = full_path.split("\\")[-1]
                                            path = tkinter.filedialog.askdirectory()
                                            if not path:
                                                return
                                            item = path + "\\" + item
                                            with open(item, "wb") as f:
                                                f.write(response.content)
                                            tkinter.messagebox.showinfo("下载完成", f"文件已下载到当前目录: {path+item}")
                                        except Exception as e:
                                            tkinter.messagebox.showinfo("错误", f"无法下载文件: {e}")
                                except Exception as e:
                                    tkinter.messagebox.showinfo("错误", f"无法打开文件夹: {e}\n{PATH}")

                    listbox.bind("<Double-Button-1>", on_double_click)
                except Exception as e:
                    tk.Label(root2, text=f"无法获取目录列表: {e}").pack()
                    print(f"无法获取目录列表: {e}")

            def download_image(self):
                """下载远程桌面图像"""
                try:
                    response = requests.get(IMAGE_URL, headers={"user-agent": get_user_agent()}, timeout=5)
                    response.raise_for_status()
                    img = Image.open(BytesIO(response.content))
                    return img
                except Exception as e:
                    logging.error(f"图像下载失败: {e}")
                    return None

            def update_image(self):
                """定期更新图像显示"""
                while self.running:
                    img = self.download_image()
                    if img and self.running:
                        # 在主线程中更新UI
                        self.root.after(0, self.update_image_ui, img)
                    time.sleep(0.1)  # 控制刷新频率

            def update_image_ui(self, img):
                """在主线程中更新图像UI"""
                try:
                    # 保持比例缩放图像以适应窗口
                    window_width = 800
                    window_height = 600
                    img_width, img_height = img.size
                    
                    # 计算缩放比例
                    ratio = min(window_width / img_width, window_height / img_height)
                    new_width = int(img_width * ratio)
                    new_height = int(img_height * ratio)
                    
                    # 高质量缩放
                    img = img.resize((new_width, new_height), Image.LANCZOS)
                    self.imgtk = ImageTk.PhotoImage(img)
                    self.label.config(image=self.imgtk)
                except Exception as e:
                    logging.error(f"图像更新失败: {e}")

            def on_close(self):
                """关闭应用程序"""
                self.running = False
                # 等待线程结束
                if self.update_thread.is_alive():
                    self.update_thread.join(timeout=1.0)
                self.root.destroy()
                logging.debug("程序正常退出")

        # 启动主应用
        root = tk.Tk()
        app = ImageApp(root)
        root.protocol("WM_DELETE_WINDOW", app.on_close)
        logging.debug("进入主应用循环")
        root.mainloop()
        
    except Exception as e:
        logging.critical(f"程序发生致命错误: {str(e)}", exc_info=True)
        # 显示错误消息
        error_root = tk.Tk()
        error_root.withdraw()  # 隐藏主窗口
        tkinter.messagebox.showerror("程序错误", f"程序发生错误: {str(e)}\n详情请查看app_debug.log")
        error_root.destroy()

if __name__ == "__main__":
    main()
